package top.loveyumin1314.music.server;

import com.google.common.base.Strings;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.handler.codec.http.*;
import io.netty.handler.codec.http.websocketx.*;
import io.netty.util.Attribute;
import top.loveyumin1314.music.bean.LotteryBean;
import top.loveyumin1314.music.bean.UserActionBean;
import top.loveyumin1314.music.bean.UserListBean;
import top.loveyumin1314.music.cmd.CreateResponse;
import top.loveyumin1314.music.code.ActionCode;
import top.loveyumin1314.music.logic.LogicDispatcher;
import top.loveyumin1314.music.lottery.LotteryService;
import top.loveyumin1314.music.lottery.LotteryServiceImpl;
import top.loveyumin1314.music.serviceimpl.BroadcastService;
import top.loveyumin1314.music.util.JacksonUtil;

import java.io.IOException;
import java.util.UUID;

/**
 * author Kerry
 * date 2017/10/10
 * desp null
 */

public class MyWebSocketServerHandler extends ChannelInboundHandlerAdapter {


    private WebSocketServerHandshaker webSocketServerHandshaker;


    @Override
    public void channelActive(ChannelHandlerContext ctx) throws Exception {
        BroadcastService.GROUP.add(ctx.channel());
    }

    @Override
    public void channelRead(ChannelHandlerContext ctx, Object msg) throws Exception {

        if (msg instanceof FullHttpRequest) { // 传统的HTTP接入
            handleHttpRequest(ctx, (FullHttpRequest) msg);
        } else if (msg instanceof WebSocketFrame) { // WebSocket接入
            if (!BroadcastService.GROUP.contains(ctx.channel())) {
                ctx.channel().close();
                return;
            }
            handleWebSocketFrame(ctx, (WebSocketFrame) msg);
        }
    }


    /**
     * 处理Http请求，完成WebSocket握手<br/>
     * 注意：WebSocket连接第一次请求使用的是Http
     *
     * @param ctx
     * @param request
     * @throws Exception
     */
    private void handleHttpRequest(ChannelHandlerContext ctx, FullHttpRequest request) throws Exception {

        System.out.println(ctx);
        System.out.println(request);

        // 如果HTTP解码失败，返回HHTP异常
        if (!request.decoderResult().isSuccess() || (!"websocket".equals(request.headers().get("Upgrade")))) {
            return;
        }

        // 正常WebSocket的Http连接请求，构造握手响应返回

        WebSocketServerHandshakerFactory wsFactory = new WebSocketServerHandshakerFactory("ws://" + request.headers().get("Host"), null, false);

        webSocketServerHandshaker = wsFactory.newHandshaker(request);

        if (webSocketServerHandshaker == null) {
            // 无法处理的websocket版本
            WebSocketServerHandshakerFactory.sendUnsupportedVersionResponse(ctx.channel());
        } else {
            // 向客户端发送websocket握手,完成握手
            webSocketServerHandshaker.handshake(ctx.channel(), request);
        }
    }


    /**
     * 处理Socket请求
     *
     * @param ctx
     * @param frame
     */
    private void handleWebSocketFrame(ChannelHandlerContext ctx, WebSocketFrame frame) throws IOException {
        // 判断是否是关闭链路的指令
        if (frame instanceof CloseWebSocketFrame) {
            webSocketServerHandshaker.close(ctx.channel(), (CloseWebSocketFrame) frame.retain());
            return;
        }
        // 判断是否是Ping消息
        if (frame instanceof PingWebSocketFrame) {
            ctx.channel().writeAndFlush(new PongWebSocketFrame(frame.content().retain()));
            return;
        }
        // 只支持文本消息
        if (!(frame instanceof TextWebSocketFrame)) {
            throw new UnsupportedOperationException("只支持文本消息");
        }

        String text = ((TextWebSocketFrame) frame).text();
        UserActionBean actionBean = JacksonUtil.data2ActionBean(text);
        System.out.println("收到:" + actionBean);


        Attribute<UserActionBean> attr = ctx.channel().attr(BroadcastService.SISSION);
        UserActionBean session = attr.get();


        final LogicDispatcher logicDispatcher = LogicDispatcher.instance();

        if (session == null) {
            //第一次,生成userId
            actionBean.setUserId(UUID.randomUUID().toString());
            if (actionBean.getActionId().equals(ActionCode.LOGIN.actionId)) {

                attr.setIfAbsent(actionBean);
                CreateResponse response = CreateResponse.create();
                ctx.writeAndFlush(response.setUserActionBeanWithActionCode(actionBean, ActionCode.M_LOGIN_SUCCESS).buildWebSocketFrame());


                //通知所有人上线了
                BroadcastService.getInstance().broadcast(response.setUserActionBeanWithActionCode(actionBean, ActionCode.BC_ONLINE));

                //返回所有人列表给自己
                UserListBean<UserActionBean> onlineUserList = BroadcastService.getInstance().getOnlineUserList(actionBean, ActionCode.BC_ONLINE_LIST);
                ctx.writeAndFlush(response.buildWebSocketFrame(onlineUserList));

                //返回加入抽奖的人列表
                UserListBean<LotteryBean> lotteryList = BroadcastService.getInstance().getLotteryList(actionBean, ActionCode.GET_LOTTORY_LIST);
                ctx.writeAndFlush(response.buildWebSocketFrame(lotteryList));

            } else {
                //第一条指令不是登录指令??
                ctx.channel().writeAndFlush(CreateResponse.create().setActionInfo(ActionCode.ILLEGAL).setData("第一条指令不是登录指令").buildWebSocketFrame());
                ctx.channel().close();
            }
        } else {
            session.setTime(System.currentTimeMillis());

            Object obj = logicDispatcher.handleMessage(actionBean);
            if (obj != null) {
                TextWebSocketFrame textWebSocketFrame = CreateResponse.create().buildWebSocketFrame(obj);
                ctx.channel().writeAndFlush(textWebSocketFrame);
            }

        }
    }


    @Override
    public void channelInactive(ChannelHandlerContext ctx) throws Exception {
        System.out.println("channelInactive.ctx = [" + ctx + "]");
        Attribute<UserActionBean> attr = ctx.channel().attr(BroadcastService.SISSION);
        if (attr != null) {
            UserActionBean userActionBean = attr.get();
            BroadcastService.getInstance().broadcast(CreateResponse.create().setUserActionBeanWithActionCode(userActionBean, ActionCode.BC_LOGOUT));
        } else {
            UserListBean<UserActionBean> onlineUserList = BroadcastService.getInstance().getOnlineUserList(ActionCode.BC_LOGOUT_LIST);
            BroadcastService.getInstance().broadcast(onlineUserList);
        }
        BroadcastService.GROUP.remove(ctx.channel());
        if (!LotteryService.IS_START_LOTTERY.get()) {
            if (BroadcastService.GROUP.isEmpty()) {
                LotteryService.LOTTERY_USERS.clear();
            }
        }
    }

    @Override
    public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
        cause.printStackTrace();
    }
}
